package com.hitqz.robot.dispatch.netty.listener;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.google.common.cache.Cache;
import com.hitqz.robot.admin.api.dto.RequireClientLogDto;
import com.hitqz.robot.api.business.dto.CmdRobot2PosDto;
import com.hitqz.robot.api.business.dto.RobotJobDto;
import com.hitqz.robot.api.business.dto.RobotSendClientMsgDto;
import com.hitqz.robot.api.business.dto.SubscriptTopicDto;
import com.hitqz.robot.api.business.entity.JobCachesEntity;
import com.hitqz.robot.api.business.entity.RobotEntity;
import com.hitqz.robot.api.business.entity.RobotGroupEntity;
import com.hitqz.robot.api.business.entity.RobotJobEntity;
import com.hitqz.robot.api.common.dto.info.ReleaseResourceDto;
import com.hitqz.robot.api.dispatch.model.MessageDto;
import com.hitqz.robot.api.dispatch.protocol.ProtocolEnum;
import com.hitqz.robot.api.dispatch.protocol.dto.*;
import com.hitqz.robot.biz.service.JobCachesService;
import com.hitqz.robot.biz.service.RobotGroupService;
import com.hitqz.robot.biz.service.RobotJobService;
import com.hitqz.robot.biz.service.RobotService;
import com.hitqz.robot.common.core.constant.CommonConstants;
import com.hitqz.robot.common.core.util.R;
import com.hitqz.robot.dispatch.netty.ChannelCache;
import com.hitqz.robot.dispatch.netty.factory.JobAlgorithmFactory;
import com.hitqz.robot.dispatch.netty.model.DispatchJobDto;
import com.hitqz.robot.dispatch.netty.service.DispatchService;
import com.hitqz.robot.dispatch.netty.util.LockUtil;
import com.hitqz.robot.dispatch.netty.util.RobotReplyUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author xupkun
 * @date 2024/7/10
 */
@Component
@Slf4j
@AllArgsConstructor
public class Send2ClientListener {

    private final RobotJobService robotJobService;

    private final DispatchService dispatchService;

    private final RobotService robotService;

    private final JobCachesService jobCachesService;

    private final JobAlgorithmFactory jobAlgorithmFactory;

    private final RobotGroupService robotGroupService;

    /**
     * 发送消息给客户端
     *
     * @param dto
     */
    @EventListener
    @Async("serverSendThreadPool")
    public void handlerSend(RobotSendClientMsgDto dto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setClientId(dto.getClientId());
        messageDto.setCmd(dto.getCmd().shortValue());
        messageDto.setSn(dto.getSeq() != null ? dto.getSeq() : 0);
        if (dto.getData() != null) {
            String data = JSONObject.toJSONString(dto.getData());
            messageDto.setData(data.getBytes(StandardCharsets.UTF_8));
        }
        ChannelCache.send2Client(dto.getClientId(), messageDto);
    }


    @EventListener
    @Async("serverSendThreadPool")
    public void handlerSend(CmdRobot2PosDto dto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.ROBOT_NAV_POS_CMD.getCmd());
        CmdRobot2PosDto posDto = new CmdRobot2PosDto();
        posDto.setPosCode(dto.getPosCode());
        messageDto.setData(JSONObject.toJSONString(posDto).getBytes(StandardCharsets.UTF_8));
        messageDto.setClientId(dto.getClientId());
        ChannelCache.send2Client(dto.getClientId(), messageDto);
    }

    /**
     * 修改机器人信息
     *
     * @param robotEntity
     */
    @EventListener
    @Async("serverSendThreadPool")
    public void handlerRobotModel(RobotEntity robotEntity) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.ROBOT_INFO.getCmd());
        RobotDto robotDto = new RobotDto();
        BeanUtil.copyProperties(robotEntity, robotDto, false);
        messageDto.setData(JSONObject.toJSONString(robotDto).getBytes(StandardCharsets.UTF_8));
        messageDto.setClientId(robotEntity.getRobotSn());
        ChannelCache.send2Client(robotDto.getRobotSn(), messageDto);
    }

    @EventListener
    @Async("serverSendThreadPool")
    public void listenerLog(AskLogMessageDto reportLogMessageDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.REPORT_LOG_MESSAGE.getCmd());
        messageDto.setData(JSONObject.toJSONString(reportLogMessageDto).getBytes(StandardCharsets.UTF_8));
        messageDto.setClientId(reportLogMessageDto.getClientId());
        ChannelCache.send2Client(reportLogMessageDto.getClientId(), messageDto);
    }


    @EventListener
    @Async("serverSendThreadPool")
    public void listenerLog(RequireClientLogDto requireClientLogDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setCmd(ProtocolEnum.UPLOAD_LOG_FILE.getCmd());
        messageDto.setData(JSONObject.toJSONString(requireClientLogDto).getBytes(StandardCharsets.UTF_8));
        messageDto.setClientId(requireClientLogDto.getClientId());
        ChannelCache.send2Client(requireClientLogDto.getClientId(), messageDto);
    }

    @EventListener
    public void handlerDoNext(RobotNextStepDto robotNextStepDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(0);
        messageDto.setClientId(robotNextStepDto.getClientId());
        messageDto.setCmd(ProtocolEnum.CMD_NEXT_STEP.getCmd());
        messageDto.setData(JSONObject.toJSONString(robotNextStepDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2Client(robotNextStepDto.getClientId(), messageDto);
    }


    //    @EventListener
//    @Async("serverSendThreadPool")
    public void handler(RobotJobDto robotJobDto) {
        //log.info("收到任务调度，启动任务:" + robotJobDto.getId());
        RobotJobEntity robotJobEntity = robotJobService.getById(robotJobDto.getId());
        RobotGroupEntity robotGroupEntity = robotGroupService.getById(robotJobEntity.getGroupId());
        String robotSn = robotJobDto.getClientId();
        if (StrUtil.isEmpty(robotSn)) {
            DispatchJobDto dto = jobAlgorithmFactory.getRoutingStrategy(robotGroupEntity.getJobAlgorithm()).dispatchJob(robotGroupEntity.getId(), robotJobEntity.getContent());
            robotSn = dto.getClientId();
        }
        RobotEntity robotEntity = robotService.selectByRobotSn(robotSn);
        if (robotEntity == null) {
            // 缓存 等待下一次执行
            //Cache<String, LinkedList<RobotJobDto>> posInfoDtoCache = SpringUtil.getBean("taskList");
            //LinkedList<RobotJobDto> robotJobEntities = posInfoDtoCache.getIfPresent("task");
            //robotJobEntities.add(robotJobDto);
            //posInfoDtoCache.put("task", robotJobEntities);
        } else {
            Cache<Integer, RobotEntity> executeRobot = SpringUtil.getBean("executeTaskRobot");
            executeRobot.put(robotJobEntity.getId(), robotEntity);

            MessageDto messageDto = new MessageDto();
            messageDto.setSn(0);
            messageDto.setCmd(ProtocolEnum.ROBOT_CMD_TICKETS.getCmd());
            RobotCmdTicketsDto robotCmdTicketsDto = new RobotCmdTicketsDto();
            robotCmdTicketsDto.setTaskId(robotJobDto.getId());
            robotCmdTicketsDto.setTaskContent(robotJobEntity.getContent());
            robotCmdTicketsDto.setTaskName(robotJobEntity.getName());
            robotCmdTicketsDto.setClientId(robotEntity.getRobotSn());
            robotCmdTicketsDto.setTaskNo(robotJobDto.getTaskNo());
            messageDto.setClientId(robotEntity.getRobotSn());
            messageDto.setData(JSONObject.toJSONString(robotCmdTicketsDto).getBytes(StandardCharsets.UTF_8));
            Lock lock = LockUtil.ROBOT_CMD_TICKETS_LOCKS.get(robotEntity.getRobotSn(), ProtocolEnum.ROBOT_CMD_TICKETS.getCmd());
            if (lock == null) {
                lock = new ReentrantLock();
                LockUtil.ROBOT_CMD_TICKETS_LOCKS.put(robotEntity.getRobotSn(), ProtocolEnum.ROBOT_CMD_TICKETS.getCmd(), lock);
            }
            synchronized (lock) {
                try {
                    //释放除自身所在资源
                    dispatchService.releaseResourceExcludeSelf(new ReleaseResourceDto(messageDto.getClientId()));

                    ChannelCache.send2Client(messageDto.getClientId(), messageDto);
                    lock.wait(2000);
                    R result = RobotReplyUtil.COMMON_REPLY_TABLE.get(robotEntity.getRobotSn(), ProtocolEnum.ROBOT_CMD_TICKETS.getCmd());
                    if (result.getCode() == CommonConstants.FAIL) {
                        //throw  new IllegalArgumentException(result.getMsg());
                    }
                } catch (Exception e) {

                }
            }
        }
    }

    private static final String lock = "robot_task_lock";

    /**
     * 一直尝试启动任务
     */
    @Scheduled(fixedDelay = 3000L)
    public void checkJob() {
        Cache<Integer, List<RobotEntity>> posInfoDtoCache = SpringUtil.getBean("robotDispatch");
        for (Integer groupId : posInfoDtoCache.asMap().keySet()) {
            synchronized (lock) {
                JobCachesEntity jobCachesEntityList = jobCachesService.getOne(Wrappers.<JobCachesEntity>lambdaQuery()
                        .eq(JobCachesEntity::getGroupId, groupId)
                        .orderByAsc(JobCachesEntity::getSn)
                        .last(" limit 1"));
                if (jobCachesEntityList != null) {
                    this.handler(jobCachesEntityList);
                }
            }
        }
    }

    //    @EventListener
//    @Async("serverSendThreadPool")
    public void handler(JobCachesEntity jobCachesEntity) {
        synchronized (jobCachesEntity.getGroupId()) {
            RobotGroupEntity robotGroupEntity = robotGroupService.getById(jobCachesEntity.getGroupId());
            DispatchJobDto dto = jobAlgorithmFactory.getRoutingStrategy(robotGroupEntity.getJobAlgorithm()).dispatchJob(robotGroupEntity.getId(), jobCachesEntity.getTaskContent());
            if (dto == null) {
                return;
            }
            if (dto.getJobId() == null) {
                dto.setJobId(jobCachesEntity.getId());
            }
            JobCachesEntity executeTask = jobCachesService.getById(dto.getJobId());
            if (executeTask == null) {
                //
                executeTask = jobCachesEntity;
            }
            // 删除缓存任务
            RobotJobEntity robotJob = new RobotJobEntity();
            robotJob.setCron("0 * * * * ?");
            robotJob.setName(executeTask.getTaskName() + "--时间：" + DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
            robotJob.setGroupId(executeTask.getGroupId());
            robotJob.setContent(executeTask.getTaskContent());
            robotJobService.save(robotJob);
            // 启动任务
            RobotJobDto robotJobDto = new RobotJobDto();
            robotJobDto.setId(robotJob.getId());
            robotJobDto.setClientId(dto.getClientId());
            robotJobDto.setTaskNo(jobCachesEntity.getTaskNo());
            try {
                this.handler(robotJobDto);
                jobCachesService.removeById(executeTask.getId());
            } catch (Exception e) {

            }
            SpringUtil.getApplicationContext().publishEvent(new SubscriptTopicDto("jobCaches"));
            //log.info("处理完任务缓存，启动任务:" + jobCachesEntity.getId());
        }

    }


    @EventListener
    @Async("serverSendThreadPool")
    public void handler(ModifySysInfoDto modifySysInfoDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(ProtocolEnum.MODIFY_SYS_INF.getCmd());
        messageDto.setCmd(ProtocolEnum.MODIFY_SYS_INF.getCmd());
        messageDto.setData(JSONObject.toJSONString(modifySysInfoDto).getBytes(StandardCharsets.UTF_8));
        ChannelCache.send2All(messageDto);
    }

    @EventListener
    @Async("serverSendThreadPool")
    public void handlerNav(RobotCmdNavDto robotCmdNavDto) {
        log.info("下发导航状态:" + JSONObject.toJSONString(robotCmdNavDto));
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(ProtocolEnum.ROBOT_NAV_CMD.getCmd());
        messageDto.setCmd(ProtocolEnum.ROBOT_NAV_CMD.getCmd());
        messageDto.setData(JSONObject.toJSONString(robotCmdNavDto).getBytes(StandardCharsets.UTF_8));
        messageDto.setClientId(robotCmdNavDto.getClientId());
        ChannelCache.send2Client(robotCmdNavDto.getClientId(), messageDto);
    }

    @EventListener
    @Async("serverSendThreadPool")
    public void handlerNav(RobotLogicOnlineDto robotCmdNavDto) {
        MessageDto messageDto = new MessageDto();
        messageDto.setSn(ProtocolEnum.ROBOT_DISPATCH_CMD.getCmd());
        messageDto.setCmd(ProtocolEnum.ROBOT_DISPATCH_CMD.getCmd());
        RobotDispatchCmdDto robotDispatchCmdDto = new RobotDispatchCmdDto();
        robotDispatchCmdDto.setLogicOnline(robotCmdNavDto.getLogicOnlineStatus());
        robotDispatchCmdDto.setClientId(robotCmdNavDto.getClientId());
        messageDto.setData(JSONObject.toJSONString(robotDispatchCmdDto).getBytes(StandardCharsets.UTF_8));
        messageDto.setClientId(robotCmdNavDto.getClientId());
        ChannelCache.send2Client(robotCmdNavDto.getClientId(), messageDto);
    }


}
